The problem nobody calls a security problem
Every new hire at an enterprise generates a burst of sensitive data: full name, national ID (CPF), personal documents, banking details, registration data. That data has to land in multiple internal systems — HR, payroll, access management, and others — before the person can actually start working.
When I picked up this project at Way-V, part of that flow was manual. Someone copied information between spreadsheets, forms, and internal systems. Everyone saw the obvious cost: onboarding took days and was error-prone.
What fewer people saw is that this was a security problem wearing an efficiency costume. Every manual copy of PII is a new attack surface. Another spreadsheet on someone's desktop. Another person with access to banking details they don't need. Another chance for data to end up somewhere unauthorized, unaudited, and unforgettable.
My job was to automate the pipeline end-to-end. My actual job, as I came to understand it, was to make sure that automating it didn't just make data leak faster.
Starting from the threat model, not the happy path
Before writing integration code, I asked a different question first: how could this pipeline fail, and what would it expose when it did?
That question shaped every design decision that followed. The controls below weren't a compliance checklist bolted on at the end — they were the architecture.
1. All input is hostile until proven otherwise
Data arrived through APIs from external sources. By definition, that's untrusted input — even when the source is a partner you like. Before anything touched business logic or an internal system, the backend enforced schema validation, type checking, normalization, and input sanitization. Malformed or unexpected payloads were rejected at the edge.
This isn't just about malicious actors. Most of what this layer catches is broken data — and broken PII propagated across five systems is its own kind of incident.
2. Encryption at rest, identity over credentials
Documents and sensitive files lived in private AWS S3 buckets with encryption at rest enabled. Access was granted through a service-specific IAM role, not user-bound credentials.
The reasoning: an application should hold exactly the permissions it needs to do its job — nothing inherited from a human, nothing permanent, nothing shared. If the service is compromised, the blast radius is the role's scope, not someone's account.
3. Data minimization: each system gets only what it needs
This was the decision I'm most proud of, because it's the one nobody asked for.
The easy design replicates the full employee payload to every destination system. The secure design asks, per integration: what does this system actually need? A system that needs name and employee ID does not receive CPF, documents, or banking data. Each integration got a contract with only its required fields.
The result: fewer copies of sensitive data in existence, smaller exposure per system, and a much easier answer to "who has access to what?"
4. Authenticated APIs, never direct database access
Integrations happened exclusively through authenticated APIs with defined contracts and per-service permissions. I deliberately avoided reaching into other systems' databases — direct access would mean tighter coupling, no audit trail, and an application that can read far more than it should.
5. Secrets stay out of the code — all of it
No credential, API key, or secret ever lived in the source code, the Git history, or deployment artifacts. Sensitive configuration was injected through environment variables, separated per environment. Boring, unglamorous, and the single most common way real companies get breached when skipped.
6. Logs are a leak vector too
Logs needed to support operational traceability without becoming a shadow database of PII. Personal data, documents, tokens, and banking details were never logged in plaintext — fields were masked or replaced with internal identifiers. You could trace that an execution happened and where it failed, without the log itself becoming sensitive.
7. Errors that help the team without helping an attacker
When an operation failed, the external response was generic: no stack traces, no internal structure, no queries, no infrastructure details. The full technical context went to internal logs, where the team could investigate. What a caller learns from your error messages is reconnaissance you handed them for free.
8. Idempotency, because retries are not optional
Distributed systems fail; networks retry. Critical operations were designed to be idempotent so that a retry never created duplicate records or reprocessed sensitive data. A duplicated employee record isn't just messy — it's a second, unmanaged copy of someone's PII.
[DIAGRAM: pipeline overview — sources → API layer (validation/sanitization) → encrypted S3 → per-system integrations with minimized payloads]
Results
The onboarding flow went from a multi-day, manual, spreadsheet-driven process to an automated, consistent, and traceable pipeline. PII stopped circulating through spreadsheets. Intermediate copies of data dropped sharply. Each system's access shrank to exactly what its function required. And across the period I operated it: zero data exposure incidents.
What this project changed for me
Somewhere in the middle of building this, I noticed that the part of the work I kept gravitating toward wasn't the automation itself. It was the adversarial questions: how does this fail? what leaks when it does? which control is missing?
Building the pipeline was the job. Protecting it was the part I couldn't stop thinking about. That realization is a big part of why I'm now moving from backend automation engineering into DevSecOps and cloud security — same systems, but with the questions I actually care about at the center.
Stack: Python, FastAPI, AWS (S3, IAM), Docker, PostgreSQL — with schema validation, secrets via environment configuration, masked structured logging, and idempotent integration design.